package org.zjx.server;

import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zjx.message.IMessage;
import org.zjx.message.KryoSerializer;
import org.zjx.message.MessageFactory;
import org.zjx.message.messages.*;

import java.io.*;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.concurrent.Future;

public abstract class ClientHandler implements Runnable {
    private static final Logger logger = LoggerFactory.getLogger(ClientHandler.class);
    protected final BaseSocketServer server;
    protected final Socket socket;
    private DataInputStream dataIn;
    private DataOutputStream dataOut;
    protected volatile boolean connectionActive = true;

    public ClientHandler(BaseSocketServer server, Socket socket, DataInputStream dataIn, DataOutputStream dataOut) {
        this.server = server;
        this.socket = socket;
        this.dataIn = dataIn;
        this.dataOut = dataOut;
    }

    public abstract void processMessage(IMessage message);

    @Override
    public void run() {
        try {
            processConnection();
        } catch (IOException e) {
            if (server.isRunning() && connectionActive) {
                logger.error("Connection error", e);
            }
        } finally {
            cleanupConnection();
        }
    }

    public void processConnection() throws IOException {
        try {
            while (isConnectionActive()) {
                try {
                    // 1. 读取长度头（带超时）
                    int length = readLengthHeader(dataIn);

                    // 2. 读取完整消息体
                    byte[] data = readMessageBody(dataIn, length);

                    // 3. 处理消息
                    IMessage message = MessageFactory.safeDeserialize(data);
                    processMessage(message);
                } catch (SocketTimeoutException e) {
                    handleSocketTimeout();
                } catch (EOFException e) {
                    logger.debug("Client disconnected normally");
                    break;
                } catch (SocketException e) {
                    handleSocketException(e);
                }
            }
        } finally {
            cleanupConnection();
        }
    }

    // 在 ClientHandler.java 中添加以下方法

    /**
     * 处理Socket超时情况
     */
    private void handleSocketTimeout() throws IOException {
        logger.debug("Socket read timeout occurred");

        // 检查连接是否仍然活跃
        if (!isConnectionActive()) {
            throw new SocketException("Connection no longer active");
        }

        // 可以在这里添加心跳检查或其他健康检查逻辑
        if (lastActivityTimeout()) {
            throw new SocketTimeoutException("Connection timed out due to inactivity");
        }
    }

    /**
     * 处理Socket异常
     */
    private void handleSocketException(SocketException e) throws IOException {
        if ("Connection reset".equals(e.getMessage())) {
            logger.debug("Client connection reset");
            throw e; // 向上抛出，由外层处理断开逻辑
        } else if ("Socket closed".equals(e.getMessage())) {
            logger.debug("Socket was closed");
            throw e;
        } else {
            logger.error("Unexpected socket exception", e);
            throw new IOException("Socket error occurred", e);
        }
    }

    /**
     * 读取消息长度头（带超时和校验）
     */
    private int readLengthHeader(DataInputStream in) throws IOException {
        long startTime = System.currentTimeMillis();
        final int headerSize = 4; // int类型的长度头占4字节

        // 等待直到有足够数据或超时
        while (in.available() < headerSize) {
            if (System.currentTimeMillis() - startTime > server.getConfig().getRequestTimeout()) {
                throw new SocketTimeoutException("Timeout waiting for length header");
            }

            // 避免忙等待
            try {
                Thread.sleep(10);
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
                throw new IOException("Interrupted while reading length header");
            }
        }

        // 读取长度头
        int length = in.readInt();

        // 验证长度合理性
        if (length <= 0) {
            throw new IOException("Invalid message length: " + length + " (<= 0)");
        }

        // 设置合理的最大消息大小限制（例如10MB）
        if (length > 10 * 1024 * 1024) {
            throw new IOException("Message too large: " + length + " bytes");
        }

        return length;
    }

    /**
     * 读取消息体内容
     */
    private byte[] readMessageBody(DataInputStream in, int length) throws IOException {
        byte[] data = new byte[length];
        int totalRead = 0;
        long startTime = System.currentTimeMillis();

        while (totalRead < length) {
            // 检查超时
            if (System.currentTimeMillis() - startTime > server.getConfig().getRequestTimeout()) {
                throw new SocketTimeoutException("Timeout reading message body");
            }

            // 检查连接状态
            if (!isConnectionActive()) {
                throw new SocketException("Connection closed while reading");
            }

            // 读取数据
            int read = in.read(data, totalRead, length - totalRead);
            if (read == -1) {
                throw new EOFException("Unexpected end of stream");
            }
            totalRead += read;
        }

        return data;
    }

    /**
     * 检查是否因不活跃而超时
     */
    private boolean lastActivityTimeout() {
        // 可以根据需要实现活动性检查
        // 例如检查最后一次收到消息的时间
        return false;
    }

    // 添加连接状态检查
    private boolean isConnectionActive() {
        return connectionActive &&
                socket != null &&
                !socket.isClosed() &&
                socket.isConnected();
    }

    protected void cleanupConnection() {
        connectionActive = false;
        server.closeQuietly(dataIn);
        server.closeQuietly(dataOut);
        server.closeQuietly(socket);
    }

    @SneakyThrows
    protected void sendMessage(IMessage message) {
        if (connectionActive) {
            byte[] data = MessageFactory.serialize(message);
            dataOut.writeInt(data.length);
            dataOut.write(data);
            dataOut.flush();
        }
    }
}